<?php

namespace app\index\controller;

use app\api\controller\Basic;

class Notify extends Basic
{
    /**
     *
     * @param int $code
     */
    protected function urnRet($return_code = "FAIL", $return_msg = "支付回调失败")
    {
        $xmlTpl = "<xml><return_code><![CDATA[%s]]></return_code><return_msg><![CDATA[%s]]></return_msg></xml>";
        $xml = sprintf($xmlTpl, $return_code, $$return_msg);
        return exit($xml);
    }

    /**
     * 支付回调
     */
    public function wxpaynotify()
    {
        ini_set('date.timezone', 'Asia/Shanghai');
        error_reporting(E_ERROR);

        $xml = file_get_contents("php://input");

        if (!$xml) {
            \think\facade\Log::record('[ wxpay_notify_error ] ' . print_r($xml, true), 'error');
            $this->urnRet('FAIL', 'xml数据异常！');
        }

        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $obj = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        $log = [
            'asynclog' => print_r($xml, true),
            'channelid' => 1,
            'type' => 1,
            'transactionid' => empty($obj['out_trade_no']) ? 0 : $obj['out_trade_no'],
            'callbackcode' => ($obj['return_code'] == 'SUCCESS') && ($obj['result_code'] == 'SUCCESS') ? 1 : 0
        ];
        $this->insertTable('payment_transaction_log', $log);

        //检验签名是否正确
        if ($this->checkSign($obj) && ($obj['return_code'] == 'SUCCESS') && ($obj['result_code'] == 'SUCCESS')) {
            \think\facade\Log::record('[ wxpay_notify_response ] 回调签名验证成功', 'info');

            //验证成功
            $ordersn = $obj['out_trade_no']; //订单号
            $money = $obj['total_fee']; //金额
            $transaction_id = $obj['transaction_id']; //微信支付订单号

            //检查该订单是否已经处理过，处理过就直接返回微信
            $order = $this->findTable('order', [
                'where' => [
                    ['isdel', 'eq', 0],
                    ['ordersn', 'eq', $ordersn],
                ],
                'order' => 'id desc',
            ]);

            $status = $order['status'];
            if ($status >= 3) {
                //返回微信这个订单已经处理完了，不必重复请求
                $this->urnRet('SUCCESS', 'OK');
            }

            \think\facade\Db::startTrans();
            //更新订单状态
            try {
                $ans = $this->updateTable('order', [
                    'where' => [
                        ['ordersn', 'eq', $ordersn],
                    ],
                    'data' => [
                        'prepayid' => $transaction_id,
                        'price' => bcdiv($money, 100, 2),
                        'paynotifytime' => time(),
                        'status' => 3,
                        'paynotifyxml' => print_r($xml, true),
                    ],
                ]);

                $row = $this->updateTable('payment_transaction', [
                    'where' => [
                        ['ordersn', 'eq', $ordersn],
                    ],
                    'data' => [
                        'payamount' => bcdiv($money, 100, 2),
                        'updatetime' => time(),
                        'status' => 1,
                    ],
                ]);

                $time = date('Y-m-d H:i:s');
                $notice = [];
                $notice[] = [
                    'type' => 2,
                    'userid' => 0,
                    'reciveutype' => 3,
                    'reciveuid' => $order['memberid'],
                    'reciveutable' => 'member',
                    'msgtitle' => "订单编号：{$ordersn}支付成功",
                    'msgtext' => "您订单编号：{$ordersn}于{$time}支付成功,赶紧邀请教练接单。"
                ];

                if ($order['isissuing'] == 0 && $order['coachid'] > 0) {
                    $notice[] = [
                        'type' => 2,
                        'userid' => 0,
                        'reciveutype' => 2,
                        'reciveuid' => $order['coachid'],
                        'reciveutable' => 'coach',
                        'msgtitle' => "代发单-订单编号：{$ordersn}支付成功",
                        'msgtext' => "您代发单-订单编号：{$ordersn}于{$time}支付成功。"
                    ];
                }

                $nrow = $this->addnotice($notice, 'two');
                if($row && $ans && $nrow) {
                    \think\facade\Db::commit();
                    $this->urnRet('SUCCESS', 'OK');
                } else {
                    \think\facade\Db::rollback();
                    \think\facade\Log::record("[ wxpay_notify_response ] order:{$row},  payment_transaction:{$ans}, notice:{$nrow}" . $ordersn, 'debug');
                    $this->urnRet('FAIL', '订单支付失败');
                }
            } catch (\Exception $e) {
                \think\facade\Db::rollback();
                \think\facade\Log::record('[ wxpay_notify_response ] 订单支付失败, 更新订单状态失败,订单号：' . $ordersn, 'debug');
                $this->urnRet('FAIL', '订单支付失败');
            }
        } else {
            \think\facade\Log::record('[ wxpay_notify_response ] 签名错误' . print_r($xml, true), 'debug');
            $this->urnRet('FAIL', '签名错误');
        }
    }


    /**
     * 支付回调
     */
    public function wxpayreward()
    {
        ini_set('date.timezone', 'Asia/Shanghai');
        error_reporting(E_ERROR);

        $xml = file_get_contents("php://input");

        if (!$xml) {
            \think\facade\Log::record('[ wxpay_notify_error ] ' . print_r($xml, true), 'error');
            $this->urnRet('FAIL', 'xml数据异常！');
        }

        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $obj = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        $log = [
            'asynclog' => print_r($xml, true),
            'channelid' => 1,
            'type' => 2,
            'transactionid' => empty($obj['out_trade_no']) ? 0 : $obj['out_trade_no'],
            'callbackcode' => ($obj['return_code'] == 'SUCCESS') && ($obj['result_code'] == 'SUCCESS') ? 1 : 0
        ];
        $this->insertTable('payment_transaction_log', $log);

        //检验签名是否正确
        if ($this->checkSign($obj) && ($obj['return_code'] == 'SUCCESS') && ($obj['result_code'] == 'SUCCESS')) {
            \think\facade\Log::record('[ wxpay_notify_response ] 回调签名验证成功', 'info');

            //验证成功
            $ordersn = $obj['out_trade_no']; //订单号
            $money = $obj['total_fee']; //金额
            $transaction_id = $obj['transaction_id']; //微信支付订单号

            //检查该订单是否已经处理过，处理过就直接返回微信
            $reward = $this->findTable('reward', [
                'where' => [
                    ['isdel', 'eq', 0],
                    ['rewardsn', 'eq', $ordersn],
                ],
                'order' => 'id desc',
            ]);

            $order = $this->findTable('order', [
                'where' => [
                    ['isdel', 'eq', 0],
                    ['orderid', 'eq', $reward['orderid']],
                ],
                'order' => 'id desc',
            ]);

            $status = $reward['status'];
            if ($status >= 2) {
                //返回微信这个订单已经处理完了，不必重复请求
                $this->urnRet('SUCCESS', 'OK');
            }
            $price = bcdiv($money, 100, 2);

            $where = ['coachid' => $reward['coachid'], 'isdel' => 0, 'status' => 1];
            $coach_amount = $this->findTable('coach_amount', ['where' => $where]);
            \think\facade\Db::startTrans();
            //更新订单状态
            try {
                $update = [
                    'price' => $price,
                    'status' => 2,
                    'paynotifytime' => time(),
                    'paynotifyxml' => print_r($xml, true),
                ];
                $row = $this->updateTable('reward', [
                    'where' => [
                        ['rewardsn', 'eq', $ordersn],
                    ],
                    'data' => $update,
                ]);

                $time = date('Y-m-d H:i:s');

                $notice = [
                    'type' => 2,
                    'userid' => 0,
                    'reciveutype' => 2,
                    'reciveuid' => $order['coachid'],
                    'reciveutable' => 'coach',
                    'msgtitle' => "您收到一条新的打赏",
                    'msgtext' => "您{$time}收到订单编号：{$order['ordersn']}的打赏{$price}元。"
                ];

                $param = [
                    'status' => 1,
                    'coachid' => $reward['coachid'],
                    'memberid' => $reward['memberid'],
                    'amount' => $reward['payamount'],
                    'type' => 1,
                    'source' => 'reward',
                    'sourceid' => $reward['id'],
                    'describe' => '订单打赏',
                ];

                $fdata = [
                    'coachid' => $reward['coachid'],
                    'money' => $reward['payamount'],
                    'type' => 3,
                ];

                $amoundata = [
                    'data' => [
                        'totalamount' => bcadd($coach_amount['totalamount'], $reward['payamount'], 2),
                    ],
                    'where' => [
                        'id' => $coach_amount['id'],
                        'revision' => $coach_amount['revision'],
                    ]
                ];
                $arow = $this->updateTable('coach_amount', $amoundata);

                $finance = $this->insertTable('finance', $fdata);
                $prow = $this->insertTable('performance', $param);
                $nrow = $this->addnotice($notice);
                if($row && $prow && $nrow && $finance && $arow) {
                    \think\facade\Db::commit();
                    $this->urnRet('SUCCESS', 'OK');
                } else {
                    if ($row == 0) {
                        $errorinfo = '订单更新';
                        $content = $update;
                    } else if ($prow == 1) {
                        $errorinfo = '业绩记录失败';
                        $content = $param;
                    } else if ($finance == 0) {
                        $errorinfo = '订单流水记录失败';
                        $content = $fdata;
                    } else if ($arow == 0) {
                        $errorinfo = '个人余额写入失败';
                        $content = $amoundata;
                    } else {
                        $errorinfo = '消息写入';
                        $content = $notice;
                    }
                    $error = [
                        'status' => 0,
                        'content' => $content,
                        'errorinfo' => $errorinfo,
                        'param' => $obj,
                    ];
                    $this->setError($error);
                    $this->urnRet('FAIL', '订单支付失败');
                }
            } catch (\Exception $e) {
                \think\facade\Db::rollback();
                \think\facade\Log::record('[ wxpay_notify_response ] 订单支付失败, 更新订单状态失败,订单号：' . $ordersn, 'debug');
                $this->urnRet('FAIL', '订单支付失败');
            }
        } else {
            \think\facade\Log::record('[ wxpay_notify_response ] 签名错误' . print_r($xml, true), 'debug');
            $this->urnRet('FAIL', '签名错误');
        }
    }

    //检查回调签名是否正确
    public function checkSign($arr)
    {
        $isNotEmptyArr = array_filter($arr);
        if (isset($isNotEmptyArr['sign'])) {
            unset($isNotEmptyArr['sign']);
        }
        //2.按照字典进行排序
        ksort($isNotEmptyArr);
        //3.组成URL键值对,使用urldecode是为了适应isNotEmptyArr中包含了中文串
        $queryStr = urldecode(http_build_query($isNotEmptyArr));
        //4.和KEY拼接
        $stringSignTemp = $queryStr . "&key=" . config('pay.wxpay.mch_key');
        //对结果进行md5之后,全部转换成大写
        $sign = strtoupper(md5($stringSignTemp));
        //返回签名
        return $sign == $arr['sign'];
    }
}
